package xin.alum.aim.groups;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.util.ReferenceCountUtil;
import lombok.SneakyThrows;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

public class Allocator {

    public static final ByteBufAllocator allocator = PooledByteBufAllocator.DEFAULT;

    private static final BlockingQueue bufferQueue = new ArrayBlockingQueue(100);

    private static final BlockingQueue toCleanQueue = new LinkedBlockingQueue();

    private static final int TO_CLEAN_SIZE = 50;

    private static final long CLEAN_PERIOD = 100;

    private static class AllocThread implements Runnable {

        @Override
        public void run() {
            long lastCleanTime = System.currentTimeMillis();
            while (!Thread.currentThread().isInterrupted()) {
                try {
                    ByteBuf buffer = allocator.buffer();
                    //确保是本线程释放
                    buffer.retain();
                    bufferQueue.put(buffer);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }

                if (toCleanQueue.size() > TO_CLEAN_SIZE || System.currentTimeMillis() - lastCleanTime > CLEAN_PERIOD) {
                    final List<ByteBuf> toClean = new ArrayList(toCleanQueue.size());
                    toCleanQueue.drainTo(toClean);
                    for (ByteBuf buffer : toClean) {
                        ReferenceCountUtil.release(buffer);
                    }
                    lastCleanTime = System.currentTimeMillis();
                }
            }
        }
    }

    static {
        Thread thread = new Thread(new AllocThread(), "qclient - redis - allocator");
        thread.setDaemon(true);
        thread.start();
    }

    @SneakyThrows
    public static ByteBuf alloc() {
        return (ByteBuf)bufferQueue.take();
    }

    public static void release(ByteBuf buf) {
        toCleanQueue.add(buf);
    }

}